#pragma once

#include <atomic>
#include "messaging/target.h"


namespace evm { namespace messaging {

using namespace std;

struct BaseIncomingMsg {
    /* Null json means empty message that do not need handling. */
    BaseIncomingMsg(Json&& msg = Json())
        : message(std::move(msg)) {}

    virtual ~BaseIncomingMsg() {}

    /* Is empty message? */
    operator bool() {
        return message.is_object();
    }

    /* Send a reply back to the client. */
    virtual void reply(Json&& msg) = 0;

    /* Send a failure back to the client. */
    virtual void reply_error(Json&& msg) = 0;

    /* Acknowledge the message.
     * Not must be overridden. */
    virtual void acknowledge() {};

    /* Requeue the message.
     * Not must be overridden. */
    /*virtual void requeue() {};*/

    Json message;
};

using IncomingMsgPtr = shared_ptr<BaseIncomingMsg>;

struct BaseListener {
    virtual ~BaseListener() {}

    virtual IncomingMsgPtr poll() = 0;

    virtual void start() {
        stopped = false;
    }

    virtual void stop() {
        stopped = true;
    }

    virtual void cleanup() {};

protected:
    atomic<bool> stopped{true};
};

using ListenerPtr = shared_ptr<BaseListener>;

using TargetPriority = pair<TargetPtr, string>;

struct BaseDriver {
    virtual ~BaseDriver() {}

    virtual void start() {}

    virtual void stop() {}

    virtual Json send(TargetPtr target, Json&& msg,
                      bool wait_for_reply = false, int timeout = -1, int retry = -1) = 0;

    virtual void send_notification(TargetPtr target, Json&& msg, int retry = -1) = 0;

    virtual ListenerPtr listen(TargetPtr target) = 0;

    virtual ListenerPtr listen_for_notifications(const vector<TargetPriority>& targets_and_priorities) = 0;

    virtual void cleanup() {}
};

using DriverPtr = shared_ptr<BaseDriver>;

}}


#include "impl_redis.h"
